﻿using AgCIM.Tools.DBLink;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinFormsApp1.Neo4jGrid
{
    public partial class GridKnownlegeForm : UserControl
    {
        #region 1-1 封装属性
        private string PGTableName
        {
            get
            {
                return this.textBox2.Text;
            }
        }
        private string LatitudeFieldName
        {
            get
            {
                return this.comboBox1.Text;
            }
        }
        private string LongitudeFieldName
        {
            get
            {
                return this.comboBox2.Text;
            }
        }
        private string NameFieldName
        {
            get
            {
                return this.comboBox3.Text;
            }
        }
        #endregion

        public GridKnownlegeForm()
        {
            InitializeComponent();
        }


        private void button2_Click(object sender, EventArgs e)
        {
            AgCIM.Tools.DBLink.ConnectionDatabase_S2 s2 = ConnectionDatabase_S2.BuildPGConnection("172.18.80.34", "5432", "ag_spatial", "agcim", "ce@2481d8945");
            var allColumns = s2.GetColumnList(PGTableName);
            if (allColumns.Contains("geometry"))
            {
                allColumns.Remove("geometry");
            }
            if (allColumns.Contains("geom"))
            {
                allColumns.Remove("geom");
            }
            var dt = s2.SelectData("select " + string.Join(",", allColumns) + " from " + PGTableName);
            this.dataGridView1.DataSource = dt;
            s2.Dispose();

            this.comboBox1.Items.Clear();
            this.comboBox2.Items.Clear();
            this.comboBox3.Items.Clear();
            for (var i = 0; i < dt.Columns.Count; i++)
            {
                this.comboBox1.Items.Add(dt.Columns[i].ColumnName);
                this.comboBox2.Items.Add(dt.Columns[i].ColumnName);
                this.comboBox3.Items.Add(dt.Columns[i].ColumnName);
            }
            for (var i = 0; i < dt.Columns.Count; i++)
            {
                if (dt.Columns[i].ColumnName.ToLower().StartsWith("lon"))
                {
                    this.comboBox2.SelectedIndex = i;
                }
                if (dt.Columns[i].ColumnName.ToLower().StartsWith("lat"))
                {
                    this.comboBox1.SelectedIndex = i;
                }
            }
        }


        private void button1_ClickAsync(object sender, EventArgs e)
        {
            button1_ClickAsync2(sender, e);
        }


        private async Task button1_ClickAsync2(object sender, EventArgs e)
        {
            try
            {
                var dt = this.dataGridView1.DataSource as DataTable;
                var lonIndex = dt.Columns.IndexOf(LongitudeFieldName);
                var latIndex = dt.Columns.IndexOf(LatitudeFieldName);
                var nameIndex = dt.Columns.IndexOf(NameFieldName);

                Dictionary<string, CommonEntity> gridEntitys = GetExistEntities();
                Dictionary<string, CommonEntity> newGridEntitys=new Dictionary<string, CommonEntity>();
                for (var i = 0; i < dt.Rows.Count; i++)
                {
                    if (dt.Rows[i][lonIndex] is DBNull) { continue; }
                    var strLon = dt.Rows[i][lonIndex].ToString();
                    var strLat = dt.Rows[i][latIndex].ToString();
                    if (string.IsNullOrEmpty(strLon)) { continue; }
                    if (string.IsNullOrEmpty(strLat)) { continue; }
                    if (double.TryParse(strLon, out double lonValue) == false) continue;
                    if (double.TryParse(strLat, out double latValue) == false) continue;

                    var gid = GetGridId(lonValue, latValue);
                    if (gridEntitys.ContainsKey(gid))
                    {
                        continue;
                    }
                    CommonEntity entity = new CommonEntity()
                    {
                        id = Guid.NewGuid().ToString(),
                    };
                    entity.name = gid;
                    entity.labels.Add("gridId(" + gid + ")");
                    gridEntitys.Add(gid, entity);
                    newGridEntitys.Add(gid , entity);
                }

                List<GridEntity> zz_auto_conver_jobs = new List<GridEntity>();
                List<CommonRelation> zz_auto_conver_relations = new List<CommonRelation>();
                string endEntityTableName = "Entity_" + PGTableName;
                for (var i = 0; i < dt.Rows.Count; i++)
                {
                    if (dt.Rows[i][lonIndex] is DBNull) { continue; }
                    var strLon = dt.Rows[i][lonIndex].ToString();
                    var strLat = dt.Rows[i][latIndex].ToString();
                    var strName = dt.Rows[i][nameIndex].ToString();
                    if (string.IsNullOrEmpty(strLon)) { continue; }
                    if (string.IsNullOrEmpty(strLat)) { continue; }
                    if (double.TryParse(strLon, out double lonValue) == false) continue;
                    if (double.TryParse(strLat, out double latValue) == false) continue;

                    var gid = GetGridId(lonValue, latValue);

                    GridEntity entity = new GridEntity()
                    {
                        id = Guid.NewGuid().ToString(),
                        name = strName,
                    };
                    entity.TableRowId = PGTableName + "(" + i.ToString() + ")";
                    var strJson = new Dictionary<string, string>();

                    for (var j = 0; j < dt.Columns.Count; j++)
                    {
                        if (dt.Rows[i].ItemArray[j] is DBNull) { continue; }
                        var str = dt.Rows[i].ItemArray[j].ToString();
                        strJson.Add(dt.Columns[j].ColumnName, str);
                    }
                    entity.entityProperities = JsonSerializer.Serialize(strJson);

                    zz_auto_conver_jobs.Add(entity);
                    zz_auto_conver_relations.Add(new CommonRelation()
                    {
                        startNode = gridEntitys[gid].id,
                        endNode = entity.id,
                        id = Guid.NewGuid().ToString(),
                        type = "Rel_Grid_" + PGTableName,
                        startNodeType = GridNameUtils.FromEntityTableName,
                        endNodeType = endEntityTableName
                    });
                }

                KnowlgeMapToNeo4j<CommonEntity> knowlgeMapToNeo4J = new KnowlgeMapToNeo4j<CommonEntity>();
                knowlgeMapToNeo4J.EntityTableName = GridNameUtils.FromEntityTableName;
                knowlgeMapToNeo4J.EntityName = GridNameUtils.FromEntityTableName;
                knowlgeMapToNeo4J.Initialize();
                await knowlgeMapToNeo4J.AddTaskEntityAsync(newGridEntitys.Values.ToList());
                knowlgeMapToNeo4J.Dispose();

                KnowlgeMapToNeo4j<GridEntity> knowlgeMapToNeo4J2 = new KnowlgeMapToNeo4j<GridEntity>();
                knowlgeMapToNeo4J2.EntityTableName = endEntityTableName;
                knowlgeMapToNeo4J2.EntityName = endEntityTableName;
                knowlgeMapToNeo4J2.Initialize();
                await knowlgeMapToNeo4J2.AddTaskEntityAsync(zz_auto_conver_jobs.ToList());

                await knowlgeMapToNeo4J2.CreateRelationshipAsync(zz_auto_conver_relations);
                knowlgeMapToNeo4J2.Dispose();
                MessageBox.Show("完成");
            }
            catch (Exception err)
            {
                MessageBox.Show("错误;" + err.Message);
            }

        }

        #region 2-1 避免重复GridID
        private Dictionary<string,CommonEntity> GetExistEntities()
        {
            var m_client = new Neo4JServe();
            m_client.Initialize();
            Neo4JTableReader reader = new Neo4JTableReader()
            {
                TableName = GridNameUtils.FromEntityTableName,
                Client = m_client.Client
            };
            var dt = reader.GetEntities();
            Dictionary<string, CommonEntity> rs = new Dictionary<string, CommonEntity>();
            if (dt == null)
            {
                reader.Client.Dispose();
                return rs;
            }
            
            for (var i=0;i<dt.Rows.Count;i++)
            {
                var entity = dt.Rows[i];
                var key = entity["id"].ToString();
                var name= entity["name"].ToString();
                rs.Add(name, new CommonEntity() { name = name, id = key });
            }

            reader.Client.Dispose();

            return rs;
        } 
        #endregion


        private const double MetersPerDegreeLat = 111000.0;

        /// <summary>
        /// 根据经纬度返回该点所在的 200m x 200m 网格ID，如 "G12_34"
        /// </summary>
        /// <param name="longitude">经度，范围 [-180, 180]</param>
        /// <param name="latitude">纬度，范围 [-90, 90]</param>
        /// <returns>网格ID字符串</returns>
        public string GetGridId(double longitude, double latitude)
        {
            if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180)
                throw new ArgumentException("Invalid latitude or longitude.");

            // 1. 计算 200米对应的纬度步长（全球近似固定）
            double deltaLat = 200.0 / MetersPerDegreeLat; // ≈ 0.0018018°

            // 2. 计算当前纬度下 200米对应的经度步长
            double metersPerLon = 111000.0 * Math.Cos(latitude * Math.PI / 180.0);
            double deltaLon = 200.0 / metersPerLon;

            // 3. 计算网格索引
            int gridI = (int)(latitude / deltaLat);
            int gridJ = (int)(longitude / deltaLon);

            // 4. 生成网格ID，如 G12_34
            return $"G{gridI}_{gridJ}";
        }

        #region 5-1 辅助工具
        private void toolStripButton1_Click(object sender, EventArgs e)
        {
            var dt = this.dataGridView1.DataSource as DataTable;
            var lonIndex = dt.Columns.IndexOf(LongitudeFieldName);
            var latIndex = dt.Columns.IndexOf(LatitudeFieldName);
            var nameIndex = dt.Columns.IndexOf(NameFieldName);

            Dictionary<string, CommonEntity> gridEntitys = new Dictionary<string, CommonEntity>();
            for (var i = 0; i < dt.Rows.Count; i++)
            {
                if (dt.Rows[i][lonIndex] is DBNull) { continue; }
                var strLon = dt.Rows[i][lonIndex].ToString();
                var strLat = dt.Rows[i][latIndex].ToString();
                if (string.IsNullOrEmpty(strLon)) { continue; }
                if (string.IsNullOrEmpty(strLat)) { continue; }
                if (double.TryParse(strLon, out double lonValue) == false) continue;
                if (double.TryParse(strLat, out double latValue) == false) continue;

                try
                {
                    var str= GetGridId(lonValue, latValue);
                    continue;
                }
                catch
                {
                    
                }
                try
                {
                    var str = GetGridId( latValue, lonValue);
                    dt.Rows[i][lonIndex] = latValue;
                    dt.Rows[i][latIndex] = lonValue;
                }
                catch
                {

                }
            }
        }
        #endregion
    }
}
